package org.x.user.service.impl;


import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.QueryWrapper;
import jakarta.servlet.http.HttpServletResponse;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.x.common.config.BusinessException;
import org.x.common.config.Result;
import org.x.user.dto.LogArchiveDTO;
import org.x.user.dto.LogQueryDTO;
import org.x.user.service.ISysLogService;
import org.x.user.entity.SysLogEntity;
import org.x.user.mapper.SysLogMapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import org.x.user.vo.LogExcelVO;

import java.net.URLEncoder;
import java.util.*;

/**
 * 操作日志表 服务层实现。
 *
 * @author mybatis-flex-helper automatic generation
 * @since 1.0
 */
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLogEntity> implements ISysLogService {

    @Autowired
    private SysLogMapper sysLogMapper;

    @Autowired
    HttpServletResponse response;

    @Async("taskExecutor")
    @Override
    public void asyncSaveLog(SysLogEntity sysLogEntity) {
        this.save(sysLogEntity);
    }

    // 分页查询
    @Override
    public Object page(LogQueryDTO logQueryDTO) {
        // 添加参数校验和默认值
        int pageNum = (logQueryDTO.getPage() == null || logQueryDTO.getPage() <= 0) ? 1 : logQueryDTO.getPage();
        int size = (logQueryDTO.getSize() == null || logQueryDTO.getSize() <= 0) ? 10 : logQueryDTO.getSize();

        //构建查询条件
        QueryWrapper wrapper = new QueryWrapper();
        if(logQueryDTO.getUserId() != null){
            wrapper.eq(SysLogEntity::getUserId, logQueryDTO.getUserId());
        }
        Page<SysLogEntity> page = sysLogMapper.paginate(pageNum,size,wrapper);

        Map<String, Object> data = new HashMap<>();
        data.put("total", page.getTotalRow());
        data.put("records", page.getRecords());
        return data;
    }

    // 详情查询
    @Override
    public Object getByIddetail(Long id) {
        // 获取当前日志信息
        SysLogEntity log = sysLogMapper.selectOneById(id);

        if (log == null) {
            throw new BusinessException("当前日志ID不存在");
        }
        return log;
    }
    // 归档
    @Transactional
    @Override
    public Object archiveLog(LogArchiveDTO archiveDTO) {
        if (archiveDTO.getRetainDays() == null) {
            throw new BusinessException("请指定要保留的日志天数");
        }
        // 计算需要归档的日志时间点
        Date archiveBeforeDate = new Date(System.currentTimeMillis() - (long) archiveDTO.getRetainDays() * 24 * 60 * 60 * 1000);

        // 查询需要归档的日志数量
        QueryWrapper queryWrapper = new QueryWrapper();
        queryWrapper.lt("created_time", archiveBeforeDate);

        // 分批处理归档
        int batchSize = archiveDTO.getBatchSize() != null ? archiveDTO.getBatchSize() : 1000;
        int archivedCount = 0;

        while (true) {
            Page<SysLogEntity> page = sysLogMapper.paginate(1, batchSize, queryWrapper);
            List<SysLogEntity> logs = page.getRecords();

            if (logs.isEmpty()) {
                break;
            }

            // 删除这批日志（实际项目中可能需要将它们移动到历史表）
            for (SysLogEntity log : logs) {
                sysLogMapper.deleteById(log.getId());
                archivedCount++;
            }

            // 如果这一页没有满，说明已经处理完所有需要归档的日志
            if (logs.size() < batchSize) {
                break;
            }
        }

        // 查询剩余日志数量
        long remainingCount = sysLogMapper.selectCountByQuery(queryWrapper);

        // 构造返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("archivedCount", archivedCount);
        result.put("remainingCount", remainingCount);

        return result;
    }

    // 导出
    @SneakyThrows
    @Override
    public void download(String startTime, String endTime) {
        // 1. 设置响应头（防止中文乱码）
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        String fileName = URLEncoder.encode("操作日志数据", "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        // 2. 构建查询条件
        QueryWrapper queryWrapper = new QueryWrapper();
        if (startTime != null && !startTime.isEmpty()) {
            queryWrapper.ge("created_time", startTime);
        }
        if (endTime != null && !endTime.isEmpty()) {
            queryWrapper.le("created_time", endTime + " 23:59:59");
        }

        // 3. 查询符合条件的所有日志
        List<SysLogEntity> logs = sysLogMapper.selectListByQuery(queryWrapper);

        // 4. 转换为导出对象
        List<LogExcelVO> data = convertToLogExcelVOList(logs);

        // 5. 使用 FastExcel 导出
        try {
            cn.idev.excel.FastExcel.write(response.getOutputStream(), LogExcelVO.class)
                    .sheet("日志列表")
                    .doWrite(data);
        }finally {
            // 确保输出流被正确关闭
            if (response.getOutputStream() != null) {
                response.getOutputStream().flush();
                response.getOutputStream().close();
            }
        }
    }
    private List<LogExcelVO> convertToLogExcelVOList(List<SysLogEntity> logs) {
        List<LogExcelVO> list = new ArrayList<>();

        for (SysLogEntity log : logs) {
            LogExcelVO logExcelVO = new LogExcelVO();
//            BeanUtils.copyProperties(log, logExcelVO);
            logExcelVO.setId(log.getId());
            logExcelVO.setUserId(log.getUserId());
            logExcelVO.setAction(log.getAction());
            logExcelVO.setTargetType(log.getTargetType());
            logExcelVO.setTargetId(log.getTargetId());

            logExcelVO.setIp(log.getIp());
            logExcelVO.setCreatedTime(log.getCreatedTime());

            list.add(logExcelVO);
        }
        return list;
    }

    // 统计
    @Override
    public Object statistics(String timeRange, String startTime, String endTime) {
        // 构建查询条件
        QueryWrapper queryWrapper = new QueryWrapper();

        // 如果有指定开始和结束时间，则使用指定的时间范围
        if (startTime != null && !startTime.isEmpty() && endTime != null && !endTime.isEmpty()) {
            queryWrapper.between("created_time", startTime, endTime + " 23:59:59");
        } else {
            // 根据timeRange设置时间范围
            Date now = new Date();
            Date startDate = calculateStartDate(now, timeRange);
            queryWrapper.ge("created_time", startDate);
        }

        // 查询符合条件的所有日志
        List<SysLogEntity> logs = sysLogMapper.selectListByQuery(queryWrapper);

        // 构造返回结果
        Map<String, Object> result = new HashMap<>();

        // 设置时间范围
        String timeRangeStr = (startTime != null && endTime != null) ?
                startTime + " 至 " + endTime :
                "最近" + ("day".equals(timeRange) ? "一天" : "week".equals(timeRange) ? "一周" : "一个月");
        result.put("timeRange", timeRangeStr);

        // 总数量
        result.put("totalCount", logs.size());
        // 操作类型排行
        result.put("topActions", getTopActions(logs));
        // 用户活跃度排行（模拟数据）
        result.put("topUsers", getTopUsers(logs));
        // 小时分布
        result.put("hourlyDistribution", getHourlyDistribution(logs));
        // 风险操作（模拟数据）
        result.put("riskOperations", getRiskOperations(logs));

        return result;
    }

    /**
     * 根据时间范围计算开始日期
     */
    private Date calculateStartDate(Date now, String timeRange) {
        long timeInMillis = now.getTime();
        switch (timeRange) {
            case "day":
                return new Date(timeInMillis - 24 * 60 * 60 * 1000L);
            case "week":
                return new Date(timeInMillis - 7 * 24 * 60 * 60 * 1000L);
            case "month":
            default:
                return new Date(timeInMillis - 30 * 24 * 60 * 60 * 1000L);
        }
    }

    /**
     * 获取操作类型排行
     */
    private List<Map<String, Object>> getTopActions(List<SysLogEntity> logs) {
        Map<String, Integer> actionCountMap = new HashMap<>();

        // 统计各操作类型的数量
        for (SysLogEntity log : logs) {
            String action = log.getAction();
            if (action != null) {
                actionCountMap.put(action, actionCountMap.getOrDefault(action, 0) + 1);
            }
        }

        // 转换为列表并排序
        List<Map<String, Object>> topActions = new ArrayList<>();
        int total = logs.size();

        actionCountMap.entrySet().stream()
                .sorted(Map.Entry.<String, Integer>comparingByValue().reversed())
                .limit(10)
                .forEach(entry -> {
                    Map<String, Object> actionData = new HashMap<>();
                    actionData.put("action", entry.getKey());
                    actionData.put("count", entry.getValue());
                    actionData.put("percent", total > 0 ? Math.round(entry.getValue() * 1000.0 / total) / 10.0 : 0);
                    topActions.add(actionData);
                });

        return topActions;
    }

    /**
     * 获取用户活跃度排行
     */
    private List<Map<String, Object>> getTopUsers(List<SysLogEntity> logs) {
        Map<Long, Integer> userCountMap = new HashMap<>();

        // 统计各用户的操作次数
        for (SysLogEntity log : logs) {
            Long userId = log.getUserId();
            if (userId != null) {
                userCountMap.put(userId, userCountMap.getOrDefault(userId, 0) + 1);
            }
        }

        // 转换为列表并排序
        List<Map<String, Object>> topUsers = new ArrayList<>();

        userCountMap.entrySet().stream()
                .sorted(Map.Entry.<Long, Integer>comparingByValue().reversed())
                .limit(10)
                .forEach(entry -> {
                    Map<String, Object> userData = new HashMap<>();
                    userData.put("userId", entry.getKey());
                    userData.put("username", "用户" + entry.getKey());
                    userData.put("count", entry.getValue());
                    topUsers.add(userData);
                });

        return topUsers;
    }

    /**
     * 获取小时分布
     */
    private List<Map<String, Object>> getHourlyDistribution(List<SysLogEntity> logs) {
        int[] hourCounts = new int[6]; // 6个时间段

        // 统计各时间段的操作次数
        for (SysLogEntity log : logs) {
            Date createdTime = log.getCreatedTime();
            if (createdTime != null) {
                int hour = createdTime.getHours();
                if (hour >= 0 && hour <= 3) {
                    hourCounts[0]++;
                } else if (hour >= 4 && hour <= 7) {
                    hourCounts[1]++;
                } else if (hour >= 8 && hour <= 11) {
                    hourCounts[2]++;
                } else if (hour >= 12 && hour <= 15) {
                    hourCounts[3]++;
                } else if (hour >= 16 && hour <= 19) {
                    hourCounts[4]++;
                } else if (hour >= 20 && hour <= 23) {
                    hourCounts[5]++;
                }
            }
        }

        // 构造返回数据
        List<Map<String, Object>> hourlyDistribution = new ArrayList<>();
        String[] timeRanges = {"00-03", "04-07", "08-11", "12-15", "16-19", "20-23"};

        for (int i = 0; i < 6; i++) {
            Map<String, Object> hourData = new HashMap<>();
            hourData.put("hour", timeRanges[i]);
            hourData.put("count", hourCounts[i]);
            hourlyDistribution.add(hourData);
        }

        return hourlyDistribution;
    }

    /**
     * 获取风险操作
     */
    private List<Map<String, Object>> getRiskOperations(List<SysLogEntity> logs) {
        List<Map<String, Object>> riskOperations = new ArrayList<>();

        // 查找风险操作（如删除用户等）
        logs.stream()
                .filter(log -> log.getAction() != null &&
                        (log.getAction().contains("DELETE") ||
                                log.getAction().contains("REMOVE") ||
                                log.getAction().contains("UPDATE")))
                .limit(10)
                .forEach(log -> {
                    Map<String, Object> riskData = new HashMap<>();
                    riskData.put("id", log.getId());
                    riskData.put("userId", log.getUserId());
                    riskData.put("action", log.getAction());
                    riskData.put("targetId", log.getTargetId());
                    riskData.put("time", log.getCreatedTime());
                    riskOperations.add(riskData);
                });

        return riskOperations;
    }

}